﻿using System.Reflection;

using Flurl;
using Flurl.Http;

namespace Tszy.Unlimited.Base.Http;

/// <summary>
/// 
/// </summary>
internal class TszyHttpClient : ITszyHttpClient, ITszyHttpRequestExecutor, ITszyHttpRequestGet, ITszyHttpRequestPost, ITszyHttpRequestPostJson
{
    private enum HttpType
    {
        Get,
        Post,
        PostJson,
    }

    private class AddFileStreamRequest
    {
        public string parameterName = string.Empty;
        public string fileName = string.Empty;
        public Stream? stream;
        public string? contentType = null;
        public int bufferSize = 4096;
    }

    private class AddFilePathRequest
    {
        public string parameterName = string.Empty;
        public string path = string.Empty;
        public string? contentType = null;
        public int bufferSize = 4096;
    }

    private readonly string _baseUrl = string.Empty;
    private string _path = string.Empty;
    private HttpType _httpType = HttpType.Get;

    // header
    private object? _headers;
    private IDictionary<string, object> _headerData = new Dictionary<string, object>();
    private string _headerAuthorizationBasicUserName = string.Empty;
    private string _headerAuthorizationBasicPassword = string.Empty;
    private string _headerAuthorizationBearerToken = string.Empty;

    // parameters
    private object? _parameters = null;
    private List<AddFileStreamRequest> _fileStreamRequests = new List<AddFileStreamRequest>();
    private List<AddFilePathRequest> _filePathRequests = new List<AddFilePathRequest>();

    /// <summary>
    /// 
    /// </summary>
    /// <param name="baseUrl"></param>
    public TszyHttpClient(string baseUrl)
    {
        _baseUrl = baseUrl ?? string.Empty;
    }

    #region --- ITszyHttpClient ---

    /// <summary>
    /// HTTP GET 请求。
    /// </summary>
    /// <param name="path">地址（不带域名）</param>
    /// <param name="parameters">参数。</param>
    /// <returns></returns>
    public ITszyHttpRequestGet Get(string path, object? parameters = null)
    {
        _path = path ?? string.Empty;
        _parameters = parameters;
        _httpType = HttpType.Get;

        return this;
    }

    /// <summary>
    /// HTTP POST 请求。
    /// </summary>
    /// <param name="path">地址（不带域名）</param>
    /// <param name="parameters">参数。</param>
    /// <returns></returns>
    public ITszyHttpRequestPost Post(string path, object? parameters = null)
    {
        _path = path ?? string.Empty;
        _parameters = parameters;
        _httpType = HttpType.Post;

        return this;
    }

    /// <summary>
    /// HTTP POST FILE 请求。
    /// </summary>
    /// <param name="path">地址（不带域名）</param>
    /// <param name="parameters">参数。</param>
    /// <returns></returns>
    public ITszyHttpRequestPostJson PostJson(string path, object? parameters = null)
    {
        _path = path ?? string.Empty;
        _parameters = parameters;
        _httpType = HttpType.PostJson;

        return this;
    }

    #endregion

    #region --- ITszyHttpRequest ---

    /// <summary>
    /// 设置请求头参数。
    /// </summary>
    /// <param name="name">参数名。</param>
    /// <param name="value">参数值。</param>
    /// <returns></returns>
    public void WithHeader(string name, object value)
    {
        if (_headerData.ContainsKey(name))
            _headerData[name] = value;
        else
            _headerData.Add(name, value);
    }

    /// <summary>
    /// 设置请求头参数。
    /// </summary>
    /// <param name="headers"></param>
    /// <returns></returns>
    public void WithHeader(object headers)
    {
        _headers = headers;
    }

    /// <summary>
    /// 设置请求头授权信息。
    /// </summary>
    /// <remarks>
    /// 会在请求头增加 Authorization = Basic XXX;
    /// </remarks>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    public void WithBasicAuth(string username, string password)
    {
        _headerAuthorizationBasicUserName = username ?? string.Empty;
        _headerAuthorizationBasicPassword = password ?? string.Empty;
    }

    /// <summary>
    /// 设置请求头授权信息。
    /// </summary>
    /// <remarks>
    /// 会在请求头增加 Authorization = Bearer token;
    /// </remarks>
    /// <param name="token"></param>
    /// <returns></returns>
    public void WithOAuthBearerToken(string token)
    {
        _headerAuthorizationBearerToken = token ?? string.Empty;
    }

    #endregion

    #region --- ITszyHttpRequestExecutor ---

    /// <summary>
    /// 请求并返回结果。
    /// </summary>
    /// <returns></returns>
    public async Task<string> ExecuteAsync()
    {
        var request = OnExecute();

        if (_fileStreamRequests.Count > 0 || _filePathRequests.Count > 0)
        {
            var response = await request.PostMultipartAsync(mp =>
            {
                if (_fileStreamRequests.Count > 0)
                {
                    foreach (var file in _fileStreamRequests)
                    {
                        mp.AddFile(file.parameterName, file.stream, file.fileName, file.contentType, file.bufferSize);
                    }
                }

                if (_filePathRequests.Count > 0)
                {
                    foreach (var file in _filePathRequests)
                    {
                        mp.AddFile(file.parameterName, file.path, file.contentType, file.bufferSize);
                    }
                }

                mp.AddStringParts(_parameters);
            });

            return await response.GetStringAsync();
        }
        else
        {
            switch (_httpType)
            {
                case HttpType.Get:
                    {
                        var response = await request.SetQueryParams(_parameters).GetAsync();

                        return await response.GetStringAsync();
                    }
                case HttpType.Post:
                    {
                        var response = await request.PostUrlEncodedAsync(_parameters);

                        return await response.GetStringAsync();
                    }
                case HttpType.PostJson:
                    {
                        var response = await request.PostJsonAsync(_parameters);

                        return await response.GetStringAsync();
                    }
            }
        }

        throw new NotImplementedException();
    }

    /// <summary>
    /// 请求并返回实例。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public async Task<T> ExecuteAsync<T>()
    {
        var request = OnExecute();

        if (_fileStreamRequests.Count > 0 || _filePathRequests.Count > 0)
        {
            var response = await request.PostMultipartAsync(mp =>
            {
                if (_fileStreamRequests.Count > 0)
                {
                    foreach (var file in _fileStreamRequests)
                    {
                        mp.AddFile(file.parameterName, file.stream, file.fileName, file.contentType, file.bufferSize);
                    }
                }

                if (_filePathRequests.Count > 0)
                {
                    foreach (var file in _filePathRequests)
                    {
                        mp.AddFile(file.parameterName, file.path, file.contentType, file.bufferSize);
                    }
                }

                mp.AddStringParts(_parameters);
            });

            return await response.GetJsonAsync<T>();
        }
        else
        {
            switch (_httpType)
            {
                case HttpType.Get:
                    {
                        var response = await request.SetQueryParams(_parameters).GetAsync();

                        return await response.GetJsonAsync<T>();
                    }
                case HttpType.Post:
                    {
                        var response = await request.PostUrlEncodedAsync(_parameters);

                        return await response.GetJsonAsync<T>();
                    }
                case HttpType.PostJson:
                    {
                        var response = await request.PostJsonAsync(_parameters);

                        return await response.GetJsonAsync<T>();
                    }
            }
        }

        throw new NotImplementedException();
    }

    private IFlurlRequest OnExecute()
    {
        Url uri;

        if (string.IsNullOrWhiteSpace(_path))
        {
            uri = new Url(_baseUrl);
        }
        else
        {
            uri = _baseUrl.AppendPathSegment(_path);
        }

        var request = uri.WithHeader("tszy", Assembly.GetExecutingAssembly().GetName()?.Version?.ToString() ?? string.Empty);

        if (_headers != null)
        {
            request = request.WithHeaders(_headers);
        }

        if (_headerData.Count > 0)
        {
            foreach (var param in _headerData)
                request = request.WithHeader(param.Key, param.Value);
        }

        if (!string.IsNullOrWhiteSpace(_headerAuthorizationBasicUserName))
        {
            request = request.WithBasicAuth(_headerAuthorizationBasicUserName, _headerAuthorizationBasicPassword);
        }

        if (!string.IsNullOrWhiteSpace(_headerAuthorizationBearerToken))
        {
            request = request.WithOAuthBearerToken(_headerAuthorizationBearerToken);
        }

        return request;
    }

    #endregion

    #region --- ITszyHttpRequestGet, ITszyHttpRequestPost, ITszyHttpRequestPostJson ---

    /// <summary>
    /// 设置请求参数。
    /// </summary>
    /// <param name="query"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public ITszyHttpRequestExecutor SetParameters(object query)
    {
        _parameters = query;
        return this;
    }

    #endregion

    #region --- ITszyHttpRequestPostFile ---

    /// <summary>
    /// 增加文件。
    /// </summary>
    /// <param name="parameterName">参数名。</param>
    /// <param name="data">文件流。</param>
    /// <param name="fileName">文件名（带后缀名）</param>
    /// <param name="contentType"></param>
    /// <param name="bufferSize"></param>
    /// <returns></returns>
    public void AddFile(string parameterName, Stream data, string fileName, string? contentType = null, int bufferSize = 4096)
    {
        _fileStreamRequests.Add(new AddFileStreamRequest
        {
            parameterName = parameterName,
            stream = data,
            fileName = fileName,
            contentType = contentType,
            bufferSize = bufferSize
        });
    }

    /// <summary>
    /// 增加文件。
    /// </summary>
    /// <param name="parameterName">参数名。</param>
    /// <param name="path">文件完整路径（包含文件名与后缀名）。</param>
    /// <param name="contentType"></param>
    /// <param name="bufferSize"></param>
    /// <returns></returns>
    public void AddFile(string parameterName, string path, string? contentType = null, int bufferSize = 4096)
    {
        _filePathRequests.Add(new AddFilePathRequest
        {
            parameterName = parameterName,
            path = path,
            contentType = contentType,
            bufferSize = bufferSize
        });
    }

    #endregion

    ITszyHttpRequestGet ITszyHttpRequest<ITszyHttpRequestGet>.WithHeader(string name, object value)
    {
        WithHeader(name, value);
        return this;
    }

    ITszyHttpRequestGet ITszyHttpRequest<ITszyHttpRequestGet>.WithHeader(object headers)
    {
        WithHeader(headers);
        return this;
    }

    ITszyHttpRequestGet ITszyHttpRequest<ITszyHttpRequestGet>.WithBasicAuth(string username, string password)
    {
        WithHeader(username, password);
        return this;
    }

    ITszyHttpRequestGet ITszyHttpRequest<ITszyHttpRequestGet>.WithOAuthBearerToken(string token)
    {
        WithHeader(token);
        return this;
    }

    ITszyHttpRequestPost ITszyHttpRequest<ITszyHttpRequestPost>.WithHeader(string name, object value)
    {
        WithHeader(name, value);
        return this;
    }

    ITszyHttpRequestPost ITszyHttpRequest<ITszyHttpRequestPost>.WithHeader(object headers)
    {
        WithHeader(headers);
        return this;
    }

    ITszyHttpRequestPost ITszyHttpRequest<ITszyHttpRequestPost>.WithBasicAuth(string username, string password)
    {
        WithHeader(username, password);
        return this;
    }

    ITszyHttpRequestPost ITszyHttpRequest<ITszyHttpRequestPost>.WithOAuthBearerToken(string token)
    {
        WithHeader(token);
        return this;
    }

    ITszyHttpRequestPostJson ITszyHttpRequest<ITszyHttpRequestPostJson>.WithHeader(string name, object value)
    {
        WithHeader(name, value);
        return this;
    }

    ITszyHttpRequestPostJson ITszyHttpRequest<ITszyHttpRequestPostJson>.WithHeader(object headers)
    {
        WithHeader(headers);
        return this;
    }

    ITszyHttpRequestPostJson ITszyHttpRequest<ITszyHttpRequestPostJson>.WithBasicAuth(string username, string password)
    {
        WithHeader(username, password);
        return this;
    }

    ITszyHttpRequestPostJson ITszyHttpRequest<ITszyHttpRequestPostJson>.WithOAuthBearerToken(string token)
    {
        WithHeader(token);
        return this;
    }

    ITszyHttpRequestPostJson ITszyHttpRequestPost.AddFile(string parameterName, Stream data, string fileName, string? contentType, int bufferSize)
    {
        AddFile(parameterName, data, fileName, contentType, bufferSize);
        return this;
    }

    ITszyHttpRequestPostJson ITszyHttpRequestPost.AddFile(string parameterName, string path, string? contentType, int bufferSize)
    {
        AddFile(parameterName, path, contentType, bufferSize);
        return this;
    }
}